package com.google.appengine.examples;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityTranslator;
import com.google.appengine.api.files.AppEngineFile;
import com.google.appengine.api.files.AppEngineFile.FileSystem;
import com.google.appengine.api.files.FileServiceFactory;
import com.google.appengine.api.files.RecordReadChannel;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * A utility to read Datastore backup files.
 */
public class DatastoreBackupReader {

  /**
   * This method should be used to read a file in leveldb log format from cloud storage. This method
   * should only be called in appengine context as it will do ACL checks before reading the file.
   * File name should be readable GS path {@code /gs/<bucket>/<key>}. See {@link FileSystem#GS} for
   * complete details.
   *
   * @param fileName qualified name for cloud store file
   * @param renderer a callback to process entity retrieved per record
   * @throws IOException
   */
  public static void readFromCloudStorage(String fileName, EntityRenderer renderer)
      throws IOException {
    if (renderer == null) {
      throw new IllegalArgumentException("Entity renderer may not be null");
    }

    AppEngineFile file = new AppEngineFile(fileName);
    RecordReadChannel readChannel;
    readChannel = FileServiceFactory.getFileService().openRecordReadChannel(file, false);
    ByteBuffer recordByte;
    while ((recordByte = readChannel.readRecord()) != null) {
      renderer.render(EntityTranslator.createFromPbBytes(recordByte.array()));
    }
  }

  /**
   * This method can be used to a file in leveldb log format which has been downloaded from cloud
   * storage (via gsutil or any other way).
   *
   * @param fileName qualified name for local file
   * @param renderer a callback to process entity retrieved per record
   * @throws IOException
   */
  public static void readFromLocal(String fileName, EntityRenderer renderer) throws IOException {
    if (renderer == null) {
      throw new IllegalArgumentException("Entity renderer may not be null");
    }

    RecordReader recordReader = new RecordReader(new LocalFileReadChannel(fileName));
    ByteBuffer recordByte;
    while ((recordByte = recordReader.readRecord()) != null) {
      renderer.render(EntityTranslator.createFromPbBytes(recordByte.array()));
    }
  }

  /**
   * Implementation of this interface should have rules to render objects from {@link Entity}. Value
   * of a property can be obtained using {@link Entity#getProperty(String)}
   */
  public interface EntityRenderer {
    /**
     * This method will iteratively be called for every entity retrieved.
     */
    void render(Entity entity);
  }
}
